/*
#import <unistd.h>	// symlink
*/

#import <CoreFoundation/CoreFoundation.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSUserDefaults.h>
#import <UIKit/UIKit.h>

#import "Application.h"
#import "PreferencesView.h"
#import "Panel.h"
#import "MousePanel.h"
#import "WinRemoconProtocol.h"
#import "keycodeutil.h"
#import "InputPanel.h"

@implementation Application

+ (void)initialize
{
	NSMutableDictionary *defaultValues = [NSMutableDictionary dictionary];
	[defaultValues setValue: [NSNumber numberWithInt: 0] forKey: @"last_panel_index"];
	[defaultValues setValue: [NSNumber numberWithInt: 0] forKey: @"host_count"];
	[defaultValues setValue: @"192.168.0.2:7378" forKey: @"current_host"];
	[defaultValues setValue: [NSNumber numberWithBool: YES] forKey: @"enable_click_sound"];
	[defaultValues setValue: [NSNumber numberWithBool: YES] forKey: @"enable_mouse_panel"];
	[defaultValues setValue: [NSNumber numberWithBool: NO] forKey: @"enable_input_panel"];
	[defaultValues setValue: [NSNumber numberWithInt: 5] forKey: @"mouse_margin"];
	[defaultValues setValue: [NSNumber numberWithBool: NO] forKey: @"mouse_move_absolute"];
	[defaultValues setValue: [NSNumber numberWithBool: YES] forKey: @"mouse_rotate_landscape"];
	[defaultValues setValue: [NSNumber numberWithBool: NO] forKey: @"scroll_using_second_finger"];
	[defaultValues setValue: @"VK_BROWSER_BACK" forKey: @"status_bar_bind_left"];
	[defaultValues setValue: @"VK_BROWSER_FORWARD" forKey: @"status_bar_bind_right"];
	[defaultValues setValue: @"VK_BROWSER_REFRESH" forKey: @"status_bar_bind_center"];
	[[NSUserDefaults standardUserDefaults] registerDefaults: defaultValues];
}

- (void)applicationDidFinishLaunching: (id)unused
{
	// defaults
	NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
	
	// frame rect
	CGRect frame = [UIHardware fullScreenApplicationContentRect];
	frame.origin = CGPointZero;
	
	// preferences view
	prefs = [[PreferencesView alloc] initWithFrame: frame parent: self];
	
	// transition view
	view = [[UITransitionView alloc] initWithFrame: frame];
	
	// panels
	NSFileManager *fileMan = [NSFileManager defaultManager];
	NSString *panelPath = DEFAULT_PANEL_PATH;	// 冗長
	NSArray *files = [fileMan directoryContentsAtPath: panelPath];
	NSMutableArray *panelArray = [NSMutableArray arrayWithCapacity: 1];
	
	// mouse panel
	if ([defaults boolForKey: @"enable_mouse_panel"])
	{
		MousePanel *mouseView = [[MousePanel alloc] initWithFrame: frame];
		[mouseView setMouseDelegate: self];
		[panelArray addObject: mouseView];
	}
	
	// input panel
	if ([defaults boolForKey: @"enable_input_panel"])
	// if (YES || [defaults boolForKey: @"enable_input_panel"])	// DEBUG
	{
		InputPanel *inputPanel = [[InputPanel alloc] initWithFrame: frame];
		[inputPanel setDelegate: self];
		[panelArray addObject: inputPanel];
	}
	
	// user panels
	for (int i = 0; i != [files count]; ++i)
	{
		NSString *filename = [files objectAtIndex: i];
		if (![filename hasSuffix: @".png"])
			continue;
		
		Panel *panel = [[Panel alloc] initWithFrame: frame image: [panelPath stringByAppendingPathComponent: filename]];
		[panel setKeyDelegate: self];
		
		[panelArray addObject: panel];
	}
	panels = [panelArray copy];
	[panels retain];
	
	panelLocked = NO;
	
	// create window
	window = [[UIWindow alloc] initWithContentRect: frame];
	[window setContentView: view];
	
	// show window
	[window orderFront: self];
	[window makeKey: self];
	[window _setHidden: NO];
	
	// show last panel if the index is in range
	{
		int lastPanelIndex = [[NSUserDefaults standardUserDefaults] integerForKey: @"last_panel_index"];
		if ([panels count] <= lastPanelIndex)
			lastPanelIndex = 0;
		
		[view transition: 0 toView: [panels objectAtIndex: currentPanelIndex = lastPanelIndex]];
		
/*
		// SSH debug
		[view transition: 0 toView: [panels objectAtIndex: 1]];
// */
	}
	
	// socket
	sock = socket(AF_INET, SOCK_DGRAM, 0);
	addr.sin_family = AF_INET;
	
	NSArray *hostTokens = [[defaults stringForKey: @"current_host"] componentsSeparatedByString: @":"];
	if ([hostTokens count] != 2)	// 0.1.9では'#'が区切文字だった
		hostTokens = [[defaults stringForKey: @"current_host"] componentsSeparatedByString: @"#"];
	if ([hostTokens count] == 2)
	{
		addr.sin_port = htons([[hostTokens objectAtIndex: 1] intValue]);
		addr.sin_addr.s_addr = inet_addr([[hostTokens objectAtIndex: 0] cStringUsingEncoding: NSASCIIStringEncoding]);
	}
}

- (void)dealloc
{
/*
	if (symlink([[[panels objectAtIndex: currentPanelIndex] imagePath] cStringUsingEncoding: NSASCIIStringEncoding], "/Applications/TouchRemocon/Default.png") != 0)	// 日本語ファイル名?
			NSLog(@"cannot create symlink for Default.png");
*/
	
	[panels release];
	[window release];
	
	[super dealloc];
	
	close(socket);
}

- (id)prevPanel
{
	if (--currentPanelIndex < 0)
		currentPanelIndex = [panels count] - 1;
	
	// save last used panel index
	[[NSUserDefaults standardUserDefaults] setInteger: currentPanelIndex forKey: @"last_panel_index"];
	
	return [panels objectAtIndex: currentPanelIndex];
}

- (id)nextPanel
{
	if (++currentPanelIndex == [panels count])
		currentPanelIndex = 0;
	
	// save last used panel index
	[[NSUserDefaults standardUserDefaults] setInteger: currentPanelIndex forKey: @"last_panel_index"];
	
	return [panels objectAtIndex: currentPanelIndex];
}

- (id)currentPanel
{
	return [panels objectAtIndex: currentPanelIndex];
}

- (void)mouseMoveAbsolute: (CGPoint)p
{
	if (![[NSUserDefaults standardUserDefaults] boolForKey: @"mouse_move_absolute"])
		return;
	
	unsigned char data[5];
	unsigned short *dp = (unsigned short *)&data[1];
	if ([[NSUserDefaults standardUserDefaults] boolForKey: @"mouse_rotate_landscape"])
	{
		*dp++ = htons((unsigned short)p.y);
		*dp = htons((unsigned short)USHRT_MAX - p.x);
	}
	else
	{
		*dp++ = htons((unsigned short)p.x);
		*dp = htons((unsigned short)p.y);
	}
	data[0] = MOUSE_MOVE_ABSOLUTE_WORD;
	
	sendto(sock, data, 5, 0, (struct sockaddr *)&addr, sizeof(addr));
}

- (void)mouseMoveRelative: (CGPoint)d
{
	if ([[NSUserDefaults standardUserDefaults] boolForKey: @"mouse_move_absolute"])
		return;
	
	unsigned char data[3];
	data[0] = MOUSE_MOVE_RELATIVE;
	if([[NSUserDefaults standardUserDefaults] boolForKey: @"mouse_rotate_landscape"])
	{
		data[1] = (signed char)d.y;
		data[2] = (signed char)(- d.x);
	}
	else
	{
		data[1] = (signed char)d.x;
		data[2] = (signed char)d.y;
	}
	
	sendto(sock, data, 3, 0, (struct sockaddr *)&addr, sizeof(addr));
}

- (void)mouseWheel: (int)rev
{
	unsigned char data[2];
	
	data[0] = MOUSE_WHEEL;
	data[1] = (signed char)rev;
	
	sendto(sock, data, 2, 0, (struct sockaddr *)&addr, sizeof(addr));
}

- (void)mouseLeftButtonClick
{
	unsigned char data[2];
	unsigned char flag = MOUSE_LEFT_BUTTON;
	
	data[0] = MOUSE_BUTTON_CLICK;
	data[1] = flag;
	
	sendto(sock, data, 2, 0, (struct sockaddr *)&addr, sizeof(addr));
}

- (void)mouseLeftButtonPress
{
	unsigned char data[2];
	unsigned char flag = MOUSE_LEFT_BUTTON;
	
	data[0] = MOUSE_BUTTON_PRESS;
	data[1] = flag;
	
	sendto(sock, data, 2, 0, (struct sockaddr *)&addr, sizeof(addr));
}

- (void)mouseLeftButtonRelease
{
	unsigned char data[2];
	unsigned char flag = MOUSE_LEFT_BUTTON;
	
	data[0] = MOUSE_BUTTON_RELEASE;
	data[1] = flag;
	
	sendto(sock, data, 2, 0, (struct sockaddr *)&addr, sizeof(addr));
}

- (void)mouseRightButtonClick
{
	unsigned char data[2];
	unsigned char flag = MOUSE_RIGHT_BUTTON;
	
	data[0] = MOUSE_BUTTON_CLICK;
	data[1] = flag;
	
	sendto(sock, data, 2, 0, (struct sockaddr *)&addr, sizeof(addr));
}

- (void)mouseRightButtonPress
{
	unsigned char data[2];
	unsigned char flag = MOUSE_RIGHT_BUTTON;
	
	data[0] = MOUSE_BUTTON_PRESS;
	data[1] = flag;
	
	sendto(sock, data, 2, 0, (struct sockaddr *)&addr, sizeof(addr));
}

- (void)mouseRightButtonRelease
{
	unsigned char data[2];
	unsigned char flag = MOUSE_RIGHT_BUTTON;
	
	data[0] = MOUSE_BUTTON_RELEASE;
	data[1] = flag;
	
	sendto(sock, data, 2, 0, (struct sockaddr *)&addr, sizeof(addr));
}

- (void)keyClicked: (unsigned char)key
{
	[self keyPressed: key];
	[self keyReleased: key];
}

- (void)keyPressed: (unsigned char)key
{
	unsigned char data[2];
	
	data[0] = KEY_PRESS;
	data[1] = key;
	
	if ([[NSUserDefaults standardUserDefaults] boolForKey: @"enable_click_sound"])
		AudioServicesPlaySystemSound(1105);
	
	sendto(sock, data, 2, 0, (struct sockaddr *)&addr, sizeof(addr));
}

- (void)keyReleased: (unsigned char)key
{
	unsigned char data[2];
	
	data[0] = KEY_RELEASE;
	data[1] = key;
	
	sendto(sock, data, 2, 0, (struct sockaddr *)&addr, sizeof(addr));
}

- (void)statusBarMouseDown: (GSEventRef)event
{
	statusBarMouseDownPoint = GSEventGetLocationInWindow(event).origin;
}

- (BOOL)isInStatusBarLeftSegment: (CGPoint)p
{
	CGSize screen = [UIHardware fullScreenApplicationContentRect].size;
	
	if (p.x < screen.width / 3 && p.y <= 20)
		return YES;
	
	return NO;
}

- (BOOL)isInStatusBarCenterSegment: (CGPoint)p
{
	CGSize screen = [UIHardware fullScreenApplicationContentRect].size;
	
	if (screen.width / 3 < p.x && p.x < screen.width * 2 / 3 && p.y <= 20)
		return YES;
	
	return NO;
}

- (BOOL)isInStatusBarRightSegment: (CGPoint)p
{
	CGSize screen = [UIHardware fullScreenApplicationContentRect].size;
	
	if (screen.width * 2 / 3 < p.x && p.y <= 20)
		return YES;
	
	return NO;
}

- (void)statusBarMouseUp: (GSEventRef)event
{
	CGPoint p = GSEventGetLocationInWindow(event).origin;
	CGSize screen = [UIHardware fullScreenApplicationContentRect].size;
	
	// if preferences view is showing, do nothing
	if ([prefs isShowing])
		return;
	
	// drag to switch panel
	if (32 < (p.y - statusBarMouseDownPoint.y))
	{
		[view transition: 7 toView: prefs];
		[prefs show];
	}
	else if (statusBarMouseDownPoint.x < screen.width / 2 && screen.width / 2 < p.x)
		[view transition: 2 toView: [self prevPanel]];
	else if (p.x < screen.width / 2 && screen.width / 2 < statusBarMouseDownPoint.x)
		[view transition: 1 toView: [self nextPanel]];

	// status bar bind
	if ([self isInStatusBarLeftSegment: statusBarMouseDownPoint] && [self isInStatusBarLeftSegment: p])
		[self keyClicked: virtualKeyCodeForName([[prefs leftBind] cStringUsingEncoding: NSASCIIStringEncoding])];
	else if ([self isInStatusBarCenterSegment: statusBarMouseDownPoint] && [self isInStatusBarCenterSegment: p])
		[self keyClicked: virtualKeyCodeForName([[prefs centerBind] cStringUsingEncoding: NSASCIIStringEncoding])];
	else if ([self isInStatusBarRightSegment: statusBarMouseDownPoint] && [self isInStatusBarRightSegment: p])
		[self keyClicked: virtualKeyCodeForName([[prefs rightBind] cStringUsingEncoding: NSASCIIStringEncoding])];
}

/*
	call from preferences view
*/
- (void)navigationBar: (UINavigationBar *)navigationBar poppedItem: (UINavigationItem *)item
{
	// set new IP address
	NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
	NSArray *hostTokens = [[defaults stringForKey: @"current_host"] componentsSeparatedByString: @":"];
	if ([hostTokens count] != 2)	// 0.1.9では'#'が区切文字だった
		hostTokens = [[defaults stringForKey: @"current_host"] componentsSeparatedByString: @"#"];
	if ([hostTokens count] == 2)
	{
		addr.sin_port = htons([[hostTokens objectAtIndex: 1] intValue]);
		addr.sin_addr.s_addr = inet_addr([[hostTokens objectAtIndex: 0] cStringUsingEncoding: NSASCIIStringEncoding]);
	}
	
	[view transition: 3 toView: [self currentPanel]];
}

@end
